package cn.kuporo.seckill.order.application.place.imnpl;

import cn.hutool.core.util.BooleanUtil;
import cn.kuporo.seckill.common.cache.distribute.DistributedCacheService;
import cn.kuporo.seckill.common.constants.MQConstants;
import cn.kuporo.seckill.common.constants.RedisConstants;
import cn.kuporo.seckill.common.exception.ErrorCode;
import cn.kuporo.seckill.common.exception.SeckillException;
import cn.kuporo.seckill.common.model.dto.SeckillGoodsDTO;
import cn.kuporo.seckill.common.model.enums.PlaceOrderType;
import cn.kuporo.seckill.common.model.message.TxMessage;
import cn.kuporo.seckill.common.utils.SnowFlakeFactory;
import cn.kuporo.seckill.dubbo.interfaces.goods.SeckillGoodsDubboService;
import cn.kuporo.seckill.mq.MessageSenderService;
import cn.kuporo.seckill.order.application.model.command.SeckillOrderCommand;
import cn.kuporo.seckill.order.application.place.SeckillPlaceOrderService;
import cn.kuporo.seckill.order.domain.model.entity.SeckillOrder;
import cn.kuporo.seckill.order.domain.service.SeckillOrderDomainService;
import com.alibaba.fastjson.JSONObject;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.concurrent.TimeUnit;

/**
 * @author: kuporo
 * @since: 2025/7/9
 * @description:
 */

@Service
@ConditionalOnProperty(name = "place.order.type", havingValue = "db")
@RequiredArgsConstructor
@Slf4j
public class SeckillPlaceOrderDbService implements SeckillPlaceOrderService {
    private final SeckillOrderDomainService seckillOrderDomainService;
    private final MessageSenderService messageSenderService;
    private final DistributedCacheService distributedCacheService;
    
    @DubboReference
    private SeckillGoodsDubboService seckillGoodsDubboService;
    @Override
    @Transactional
    public Long placeOrder(Long userId, SeckillOrderCommand seckillOrderCommand) {
        // 获取商品
        SeckillGoodsDTO seckillGoodsDTO = seckillGoodsDubboService.getSeckillGoods(seckillOrderCommand.getGoodsId(), seckillOrderCommand.getVersion());
        // 检测商品信息
        this.checkSeckillGoods(seckillOrderCommand, seckillGoodsDTO);
        long txNo = SnowFlakeFactory.getSnowFlakeFromCache().nextId();
        boolean exception = false;
        try{
            // 获取商品库存
            Integer availableStock = seckillGoodsDubboService.getAvailableStockById(seckillOrderCommand.getGoodsId());
            // 库存不足
            if (availableStock == null || availableStock < seckillOrderCommand.getQuantity()){
                throw new SeckillException(ErrorCode.STOCK_LT_ZERO);
            }
        }catch (Exception e){
            exception = true;
            log.error("SeckillPlaceOrderDbService|下单异常|参数:{}|异常信息:{}", JSONObject.toJSONString(seckillOrderCommand), e.getMessage());
        }
        // 发送事务消息
        messageSenderService.sendMessageInTransaction(this.getTxMessage(MQConstants.TOPIC_TX_MSG, txNo, userId, PlaceOrderType.DB, exception, seckillOrderCommand, seckillGoodsDTO, 0, seckillOrderCommand.getOrderTaskId()), null);
        return txNo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveOrderInTransaction(TxMessage txMessage) {
        try{
            Boolean submitTransaction = distributedCacheService.hasKey(RedisConstants.getKey(RedisConstants.ORDER_TX_KEY, txMessage.getTxNo()));
            if (BooleanUtil.isTrue(submitTransaction)){
                log.info("saveOrderInTransaction|已经执行过本地事务|{}", txMessage.getTxNo());
                return;
            }
            //构建订单
            SeckillOrder seckillOrder = this.buildSeckillOrder(txMessage);
            //保存订单
            seckillOrderDomainService.saveSeckillOrder(seckillOrder);
            //保存事务日志
            distributedCacheService.put(RedisConstants.getKey(RedisConstants.ORDER_TX_KEY, txMessage.getTxNo()), txMessage.getTxNo(), RedisConstants.TX_LOG_EXPIRE_DAY, TimeUnit.DAYS);
        }catch (Exception e){
            log.error("saveOrderInTransaction|异常|{}", e.getMessage());
            distributedCacheService.delete(RedisConstants.getKey(RedisConstants.ORDER_TX_KEY, txMessage.getTxNo()));
            throw e;
        }
    }
}
